package com.flame.shapeville.controller.task;

import com.flame.shapeville.bean.Exercise;
import com.flame.shapeville.bean.Task;
import com.flame.shapeville.controller.JfController;
import com.flame.shapeville.factory.BeanFactory;
import com.flame.shapeville.service.ExerciseService;
import javafx.animation.FadeTransition;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.util.Duration;

import java.util.List;

/**
 * Abstract base class for all task controllers.
 * Provides common functionality for handling exercises, navigation, and UI interactions.
 *
 * @author Flame Dev Team
 * @version 1.0
 * @since 2025-05-13
 */
public abstract class JfTaskController implements JfController {

    /**
     * Button to navigate to the previous exercise.
     */
    @FXML
    Button prev;

    /**
     * Button to navigate to the next exercise.
     */
    @FXML
    Button next;

    /**
     * Label displaying the current exercise number.
     */
    @FXML
    Label no;

    /**
     * Label displaying the question text.
     */
    @FXML
    Label question;

    /**
     * Pane where the shape or visual content is displayed.
     */
    @FXML
    Pane shape;

    /**
     * Text label used for instructional messages or feedback.
     */
    @FXML
    Text notice;

    /**
     * Input field for user answers.
     */
    @FXML
    TextField input;

    /**
     * Button to submit the current answer.
     */
    @FXML
    Button submit;

    /**
     * Fade transition animation for notice text.
     */
    FadeTransition noticeFt;

    /**
     * Service for managing exercise data.
     */
    ExerciseService exerciseService;

    /**
     * List of exercises in the current task.
     */
    List<Exercise> exercises;

    /**
     * The currently active exercise.
     */
    Exercise curExercise;

    /**
     * Index of the current exercise.
     */
    int exPointer = 0;

    /**
     * Label used for displaying a countdown timer.
     */
    @FXML
    Label clock;

    /**
     * Current time remaining for the exercise (in seconds).
     */
    int time = -1;

    /**
     * Timeline used for countdown animations.
     */
    Timeline timeline = new Timeline();

    /**
     * Initializes the controller with exercise data and sets up UI elements.
     */
    @Override
    public void init() {
        Task task = (Task) BeanFactory.getBean("curTask");
        exerciseService = (ExerciseService) BeanFactory.getBean("exerciseService");
        exercises = exerciseService.loadExercise(task.getId());
        for (int i = 0; i < exercises.size(); i++) {
            Exercise exercise = exercises.get(i);
            if (!exercise.getCompleted()) {
                exPointer = i;
                break;
            }
        }
    }

    /**
     * Abstract method to set the current exercise and update UI accordingly.
     */
    abstract void setExercise();

    /**
     * Navigates to the previous exercise.
     */
    @FXML
    void prevEx() {
        exPointer = Math.max(0, exPointer - 1);
        setExercise();
        flushSwitchButton();
    }

    /**
     * Navigates to the next exercise.
     */
    @FXML
    void nextEx() {
        exPointer = Math.min(exercises.size() - 1, exPointer + 1);
        setExercise();
        flushSwitchButton();
    }

    /**
     * Updates navigation button states based on current exercise index.
     */
    void flushSwitchButton() {
        prev.setDisable(exPointer == 0);
        prev.setVisible(exPointer != 0);
        next.setDisable(exPointer == exercises.size() - 1);
        next.setVisible(exPointer != exercises.size() - 1);
    }

    /**
     * Sets up the notice animation for feedback display.
     */
    void initNotice() {
        noticeFt = new FadeTransition(Duration.seconds(1.3), notice);
        noticeFt.setFromValue(0);
        noticeFt.setToValue(1);
        noticeFt.setCycleCount(2);
        noticeFt.setAutoReverse(true);
    }

    /**
     * Abstract method to handle submission of an answer.
     */
    @FXML
    abstract void submit();

    /**
     * Handles correct/incorrect answer logic and saves progress.
     *
     * @param answer the user's submitted answer
     */
    void answerAnimationAndSave(String answer) {
        boolean result = curExercise.answer(answer, time);
        if (result) {
            Integer misCount = curExercise.getMisCount();
            if (misCount == 0) {
                notice.setText("Excellent!!!");
            } else if (misCount == 1) {
                notice.setText("Perfect!");
            } else {
                notice.setText("Good job");
            }
            notice.setStyle("-fx-fill: orange");
            finishClock();
        } else {
            if (curExercise.getChance() == 0) {
                notice.setText("Sorry, you have no chance");
            } else {
                notice.setText("Wrong answer");
            }
            notice.setStyle("-fx-fill: red");
        }
        noticeFt.play();
        exerciseService.saveExercise(curExercise);
        flushInput();
    }

    /**
     * Handles timeout during exercise completion.
     */
    void timeoutAnimationAndSave() {
        curExercise.timeout();
        notice.setStyle("-fx-fill: red");
        notice.setText("Time out");
        noticeFt.play();
        exerciseService.saveExercise(curExercise);
        flushInput();
    }

    /**
     * Stops the countdown timer.
     */
    void finishClock() {
        timeline.stop();
    }

    /**
     * Starts the countdown timer for the current exercise.
     */
    void startClock() {
        timeline.getKeyFrames().clear();
        time = curExercise.getTime();
        if (time == -1 || time == 0) {
            return;
        }
        timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1), event -> {
            clock.setText(time + "");
            if (time > 0) {
                time--;
            } else {
                timeline.stop();
                timeoutAnimationAndSave();
            }
        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();
    }

    /**
     * Clears or enables/disables input fields based on exercise state.
     */
    abstract void flushInput();
}
